package com.xhwl.logistics.strategy.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xhwl.common.exception.BusinessException;
import com.xhwl.common.exception.DataException;
import com.xhwl.common.vo.cent.vehicle.CarVO;
import com.xhwl.logistics.bo.VehicleCarRecordInParkingLotBO;
import com.xhwl.logistics.bo.VehicleCarportBO;
import com.xhwl.logistics.bo.VehicleParkingLotSecondBO;
import com.xhwl.logistics.bo.cent.DeviceBO;
import com.xhwl.logistics.bo.cent.VehicleIntegratedConfigBO;
import com.xhwl.logistics.bo.cent.VehicleSystemInfoBO;
import com.xhwl.logistics.bo.input.QueryVehicleCarRecordInParkingLotBO;
import com.xhwl.logistics.bo.input.QueryVehicleCarportBO;
import com.xhwl.logistics.bo.manipulate.VehicleSyncBO;
import com.xhwl.logistics.constant.KeyTopConstant;
import com.xhwl.logistics.constant.VehicleConstant;
import com.xhwl.logistics.converter.mapstruct.CentBiConverter;
import com.xhwl.logistics.converter.mapstruct.CentDeviceConverter;
import com.xhwl.logistics.converter.mapstruct.VehicleAccessRecordConverter;
import com.xhwl.logistics.converter.mapstruct.VehicleCarConverter;
import com.xhwl.logistics.converter.mapstruct.VehicleCarportConverter;
import com.xhwl.logistics.converter.mapstruct.VehiclePayRecordConverter;
import com.xhwl.logistics.converter.mapstruct.VehicleSyncConverter;
import com.xhwl.logistics.dao.mapper.IVehicleParkingLotInfoMapper;
import com.xhwl.logistics.dao.mapper.IVehicleParkingLotMapper;
import com.xhwl.logistics.dto.http.*;
import com.xhwl.logistics.dto.http.query.*;
import com.xhwl.logistics.dto.query.DeviceControlDTO;
import com.xhwl.logistics.entity.VehicleArea;
import com.xhwl.logistics.entity.VehicleParkingLot;
import com.xhwl.logistics.entity.VehiclePassage;
import com.xhwl.logistics.po.VehicleAreaPO;
import com.xhwl.logistics.po.VehicleParkingLotInfoPO;
import com.xhwl.logistics.po.VehicleParkingLotPO;
import com.xhwl.logistics.po.VehiclePassageSecondPO;
import com.xhwl.logistics.service.IVehicleAccessRecordService;
import com.xhwl.logistics.service.IVehiclePayRecordService;
import com.xhwl.logistics.service.cent.IBIVehicleService;
import com.xhwl.logistics.service.cent.QueryDeviceSecondService;
import com.xhwl.logistics.service.impl.VehicleAreaServiceImpl;
import com.xhwl.logistics.service.impl.VehicleParkingLotServiceImpl;
import com.xhwl.logistics.service.impl.VehiclePassageServiceImpl;
import com.xhwl.logistics.service.impl.http.KeTopHttpServiceImpl;
import com.xhwl.logistics.strategy.IVehicleParkingLotHandler;
import com.xhwl.logistics.util.AboutDateUtil;
import com.xhwl.logistics.vo.CarBillInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Optionals;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
//@Service
public class VehicleParkingLotKeTopHandlerImpl implements IVehicleParkingLotHandler {

    @Autowired
    private KeTopHttpServiceImpl keTopHttpService;

    @Autowired
    private IVehicleParkingLotMapper vehicleParkingLotMapper;

    @Autowired
    private IVehicleParkingLotInfoMapper vehicleParkingLotInfoMapper;

    @Autowired
    private VehicleParkingLotServiceImpl vehicleParkingLotService;

    @Autowired
    private VehicleAreaServiceImpl vehicleAreaService;

    @Autowired
    private VehiclePassageServiceImpl vehiclePassageService;

    @Autowired
    private IVehicleAccessRecordService vehicleAccessRecordService;

    @Autowired
    private IVehiclePayRecordService vehiclePayRecordService;

    @Autowired
    private IBIVehicleService biVehicleService;

    @Autowired
    private QueryDeviceSecondService queryDeviceSecondService;

    @Autowired
    private VehicleCarportConverter vehicleCarportConverter;

    @Autowired
    private VehicleAccessRecordConverter vehicleAccessRecordConverter;

    @Autowired
    private VehiclePayRecordConverter vehiclePayRecordConverter;

    @Autowired
    private CentBiConverter centBiConverter;

    @Autowired
    private VehicleSyncConverter vehicleSyncConverter;

    @Autowired
    private CentDeviceConverter centDeviceConverter;

    @Autowired
    private VehicleCarConverter vehicleCarConverter;

    //道闸控制的常量
    private static final String openDevice = "0";
    private static final String closeDevice = "1";
    private static final String allOpenDevice = "2";
    private static final String cancelAllOpenDevice = "3";

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void sync(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, VehicleSyncBO vehicleSyncBO) {
        log.info("KeTop sync start!");
        log.info("vehicleIntegratedConfigBO: " + vehicleIntegratedConfigBO);
        Integer projectId = vehicleIntegratedConfigBO.getVehicleSystemInfoBO().getProjectId();

        Optional<VehicleParkingLotInfoPO> optionalVehicleParkingLotInfoPO = vehicleParkingLotInfoMapper.queryParkingLotInfoByProjectId(projectId);

        Optional<VehicleParkingLotPO> optionalVehicleParkingLotPO;
        List<String> areaThirdIdList;
        Map<String, VehicleAreaPO> mapForArea;
        List<String> passageThirdIdList;
        Map<String, VehiclePassageSecondPO> mapForPassage;
        if (optionalVehicleParkingLotInfoPO.isPresent()) {
            VehicleParkingLotInfoPO vehicleParkingLotInfoPO = optionalVehicleParkingLotInfoPO.get();
            List<VehicleParkingLotPO> vehicleParkingLotPOList = vehicleParkingLotInfoPO.getVehicleParkingLotPOList();
            Assert.isTrue(vehicleParkingLotPOList.size() == 1, "项目下，对于科拓厂商的车场只能有一个");

            VehicleParkingLotPO vehicleParkingLotPO = vehicleParkingLotPOList.get(0);
            optionalVehicleParkingLotPO = Optional.of(vehicleParkingLotPO);

            List<VehicleAreaPO> vehicleAreaPOList = vehicleParkingLotPO.getVehicleAreaPOList();
            areaThirdIdList = vehicleAreaPOList.stream()
                    .map(vehicleAreaPO -> StringUtils.substringAfter(vehicleAreaPO.getThirdAreaId(), "-"))
                    .collect(Collectors.toList());
            Assert.isTrue(!CollectionUtils.containsAny(areaThirdIdList, "", null), "已入库的areaThirdId集合中，存在null和空字符串");
            mapForArea = vehicleAreaPOList.stream()
                    .collect(Collectors.toMap(vehicleAreaPO -> StringUtils.substringAfter(vehicleAreaPO.getThirdAreaId(), "-"), Function.identity()));

            List<VehiclePassageSecondPO> vehiclePassageSecondPOList = vehicleAreaPOList.stream()
                    .flatMap(vehicleAreaPO -> vehicleAreaPO.getVehiclePassageSecondPOList().stream())
                    .collect(Collectors.toList());
            passageThirdIdList = vehiclePassageSecondPOList.stream()
                    .map(vehiclePassageSecondPO -> StringUtils.substringAfterLast(vehiclePassageSecondPO.getThirdPassageId(), "-"))
                    .collect(Collectors.toList());
            Assert.isTrue(!CollectionUtils.containsAny(passageThirdIdList, "", null), "已入库的passageThirdId集合中，存在null和空字符串");
            mapForPassage = vehiclePassageSecondPOList.stream()
                    .collect(Collectors.toMap(vehiclePassageSecondPO -> StringUtils.substringAfterLast(vehiclePassageSecondPO.getThirdPassageId(), "-"), Function.identity()));
        } else {
            optionalVehicleParkingLotPO = Optional.empty();

            areaThirdIdList = new ArrayList<>();
            mapForArea = new HashMap<>();

            passageThirdIdList = new ArrayList<>();
            mapForPassage = new HashMap<>();
        }



        //车场信息同步不考虑删除车场的情况，考虑新增，更新（一个车场车位数）
        ParkingLotInfoKeTopDTO parkingLotInfoKeTopDTO = keTopHttpService.queryParkingLotInfo(vehicleIntegratedConfigBO, new QueryCommonKeTopDTO());
        VehicleParkingLot tempForVehicleParkingLot = new VehicleParkingLot();
        Optionals.ifPresentOrElse(
                optionalVehicleParkingLotPO,
                vehicleParkingLotPO -> {
                    tempForVehicleParkingLot.setId(vehicleParkingLotPO.getId());
                    tempForVehicleParkingLot.setParkId(vehicleParkingLotPO.getParkId());

                    VehicleParkingLot vehicleParkingLot = vehicleSyncConverter.toVehicleParkingLotForUpdate(parkingLotInfoKeTopDTO, vehicleParkingLotPO.getId());
                    vehicleParkingLotService.updateById(vehicleParkingLot);
                },
                () -> {
                    VehicleParkingLot vehicleParkingLot = vehicleSyncConverter.toVehicleParkingLotForAdd(parkingLotInfoKeTopDTO, projectId);
                    vehicleParkingLotService.save(vehicleParkingLot);

                    tempForVehicleParkingLot.setId(vehicleParkingLot.getId());
                    tempForVehicleParkingLot.setParkId(vehicleParkingLot.getParkId());
                }
        );



        ParkingLotAreaKeTopDTO parkingLotAreaKeTopDTO = keTopHttpService.queryParkingLotArea(vehicleIntegratedConfigBO, new QueryCommonKeTopDTO());
        List<ParkingLotAreaInfoKeTopDTO> parkingLotAreaInfoKeTopDTOList = parkingLotAreaKeTopDTO.getAreaInfo();
        List<String> vendorAreaThirdIdList = parkingLotAreaInfoKeTopDTOList.stream()
                .map(parkingLotAreaInfoKeTopDTO -> String.valueOf(parkingLotAreaInfoKeTopDTO.getAreaCode()))
                .collect(Collectors.toList());
        Map<String, ParkingLotAreaInfoKeTopDTO> mapForVendorArea = parkingLotAreaInfoKeTopDTOList.stream()
                .collect(Collectors.toMap(parkingLotAreaInfoKeTopDTO -> String.valueOf(parkingLotAreaInfoKeTopDTO.getAreaCode()), Function.identity()));
        //区域信息同步不考虑删除区域的情况，考虑新增，更新（区域的名称，车位数）
        List<String> addForArea = ListUtils.subtract(vendorAreaThirdIdList, areaThirdIdList);
        List<String> updateForArea = ListUtils.intersection(areaThirdIdList, vendorAreaThirdIdList);
        Map<String, VehicleArea> tempForAddArea;
        if (!addForArea.isEmpty()) {
            List<ParkingLotAreaInfoKeTopDTO> add = addForArea.stream()
                    .map(areaThirdId -> {
                        ParkingLotAreaInfoKeTopDTO parkingLotAreaInfoKeTopDTO = mapForVendorArea.get(areaThirdId);
                        Assert.notNull(parkingLotAreaInfoKeTopDTO, "根据areaThirdId，找不到需要新增区域的科拓返回的信息");
                        return parkingLotAreaInfoKeTopDTO;
                    })
                    .collect(Collectors.toList());

            List<VehicleArea> vehicleAreaList = vehicleSyncConverter.toVehicleAreaListForAdd(add, tempForVehicleParkingLot.getId(), tempForVehicleParkingLot.getParkId());
            vehicleAreaService.saveBatch(vehicleAreaList);

            tempForAddArea = vehicleAreaList.stream()
                    .collect(Collectors.toMap(vehicleArea -> String.valueOf(vehicleArea.getAreaCode()), Function.identity()));
        } else {
            tempForAddArea = new HashMap<>();
        }
        if (!updateForArea.isEmpty()) {
            List<VehicleArea> vehicleAreaList = updateForArea.stream()
                    .map(areaThirdId -> {
                        VehicleAreaPO vehicleAreaPO = mapForArea.get(areaThirdId);
                        Assert.notNull(vehicleAreaPO, "根据areaThirdId，找不到需要更新区域的已入库的信息");
                        ParkingLotAreaInfoKeTopDTO parkingLotAreaInfoKeTopDTO = mapForVendorArea.get(areaThirdId);
                        Assert.notNull(parkingLotAreaInfoKeTopDTO, "根据areaThirdId，找不到需要更新区域的科拓返回的信息");

                        return vehicleSyncConverter.toVehicleAreaForUpdate(parkingLotAreaInfoKeTopDTO, vehicleAreaPO.getId());
                    })
                    .collect(Collectors.toList());

            vehicleAreaService.updateBatchById(vehicleAreaList);
        }



        ParkingLotPassageKeTopDTO parkingLotPassageKeTopDTO = keTopHttpService.queryParkingLotPassage(vehicleIntegratedConfigBO, new QueryParkingLotPassageKeTopDTO());
        Integer pageSize = parkingLotPassageKeTopDTO.getPageSize();
        Integer totalCount = parkingLotPassageKeTopDTO.getTotalCount();
        if (null!=pageSize && null!=totalCount && totalCount>pageSize) {
            throw new BusinessException("同步车场通道信息时，通道总数大于目前的分页数（pageSize）20，因此通道信息同步会不完整");
        }
        List<ParkingLotPassageInfoKeTopDTO> parkingLotPassageInfoKeTopDTOList = parkingLotPassageKeTopDTO.getNodeList();
        List<String> vendorPassageThirdIdList = parkingLotPassageInfoKeTopDTOList.stream()
                .map(parkingLotPassageInfoKeTopDTO -> String.valueOf(parkingLotPassageInfoKeTopDTO.getId()))
                .collect(Collectors.toList());
        Map<String, ParkingLotPassageInfoKeTopDTO> mapForVendorPassage = parkingLotPassageInfoKeTopDTOList.stream()
                .collect(Collectors.toMap(parkingLotPassageInfoKeTopDTO -> String.valueOf(parkingLotPassageInfoKeTopDTO.getId()), Function.identity()));
        //通道信息同步不考虑删除通道的情况，考虑新增，更新（通道的名称）
        List<String> addForPassage = ListUtils.subtract(vendorPassageThirdIdList, passageThirdIdList);
        List<String> updateForPassage = ListUtils.intersection(passageThirdIdList, vendorPassageThirdIdList);
        if (!addForPassage.isEmpty()) {
            List<VehiclePassage> vehiclePassageList = addForPassage.stream()
                    .map(passageThirdId -> {
                        ParkingLotPassageInfoKeTopDTO parkingLotPassageInfoKeTopDTO = mapForVendorPassage.get(passageThirdId);
                        Assert.notNull(parkingLotPassageInfoKeTopDTO, "根据passageThirdId，找不到需要新增通道的科拓返回的信息");

                        String areaCode = String.valueOf(parkingLotPassageInfoKeTopDTO.getAreaCode());
                        VehicleAreaPO vehicleAreaPO = mapForArea.get(areaCode);
                        VehicleArea vehicleArea = tempForAddArea.get(areaCode);
                        if (Objects.nonNull(vehicleAreaPO)) {
                            return vehicleSyncConverter.toVehiclePassageForAdd(parkingLotPassageInfoKeTopDTO, vehicleAreaPO.getId(), vehicleAreaPO.getThirdAreaId(),projectId);
                        } else if (Objects.nonNull(vehicleArea)) {
                            return vehicleSyncConverter.toVehiclePassageForAdd(parkingLotPassageInfoKeTopDTO, vehicleArea.getId(), vehicleArea.getThirdAreaId(),projectId);
                        } else {
                            throw new BusinessException("根据passageThirdId，找不到需要新增通道相关联的区域信息");
                        }
                    })
                    .collect(Collectors.toList());

            vehiclePassageService.saveBatch(vehiclePassageList);
        }
        if (!updateForPassage.isEmpty()) {
            List<VehiclePassage> vehiclePassageList = updateForPassage.stream()
                    .map(passageThirdId -> {
                        VehiclePassageSecondPO vehiclePassageSecondPO = mapForPassage.get(passageThirdId);
                        Assert.notNull(vehiclePassageSecondPO, "根据passageThirdId，找不到需要更新通道的已入库信息");
                        ParkingLotPassageInfoKeTopDTO parkingLotPassageInfoKeTopDTO = mapForVendorPassage.get(passageThirdId);
                        Assert.notNull(vehiclePassageSecondPO, "根据passageThirdId，找不到需要更新通道的科拓返回的信息");

                        return vehicleSyncConverter.toVehiclePassageForUpdate(parkingLotPassageInfoKeTopDTO, vehiclePassageSecondPO.getId(),projectId);
                    })
                    .collect(Collectors.toList());

            vehiclePassageService.updateBatchById(vehiclePassageList);
        }

        log.info("KeTop sync end!");
    }

    @Override
    public void syncDevice(VehicleIntegratedConfigBO vehicleIntegratedConfigBO) {
        log.info("KeTop sync device start!");
        log.info("vehicleIntegratedConfigBO: " + vehicleIntegratedConfigBO);

        VehicleSystemInfoBO vehicleSystemInfoBO = vehicleIntegratedConfigBO.getVehicleSystemInfoBO();
        Integer projectId = vehicleSystemInfoBO.getProjectId();
        Integer productId = vehicleSystemInfoBO.getProductId();
        Integer productCategoryId = vehicleSystemInfoBO.getProductCategoryId();

        ParkingLotDeviceKeTopDTO parkingLotDeviceKeTopDTO = keTopHttpService.queryParkingLotDevice(vehicleIntegratedConfigBO, new QueryParkingLotDeviceKeTopDTO());
        List<ParkingLotDeviceInfoKeTopDTO> deviceListFromKeTop = parkingLotDeviceKeTopDTO.getDeviceList();

        List<DeviceBO> deviceListFromCentDevice = queryDeviceSecondService.queryDevicesByProjectId(projectId);
        Map<String, Integer> temp = deviceListFromCentDevice.stream().collect(Collectors.toMap(DeviceBO::getNumber, DeviceBO::getId));

        //获取通道信息，将设备与通道进行关联
        List<VehiclePassage> vehiclePassageList = vehiclePassageService.list(new LambdaQueryWrapper<VehiclePassage>().eq(VehiclePassage::getProjectId, vehicleIntegratedConfigBO.getVehicleSystemInfoBO().getProjectId()));
        Map<String, VehiclePassage> mapForPassage = vehiclePassageList.stream().collect(Collectors.toMap(s -> s.getRemark(), Function.identity()));
        List<String> ipFromPassage = vehiclePassageList.stream().map(s -> s.getRemark()).collect(Collectors.toList());

        List<DeviceBO> add = new ArrayList<>();
        List<DeviceBO> update = new ArrayList<>();
        deviceListFromKeTop.forEach(parkingLotDeviceInfoKeTopDTO -> {
            Optionals.ifPresentOrElse(
                    Optional.ofNullable(temp.get(parkingLotDeviceInfoKeTopDTO.getDeviceCode())),
                    id -> {
                        //将主相机的ip与之关联通道进行关联，方便获取设备地址
                        String ip = ipFromPassage.stream().filter(s -> {
                            if (StringUtils.equals(s, parkingLotDeviceInfoKeTopDTO.getDeviceIp())) {
                                return true;
                            } else {
                                return false;
                            }
                        }).findAny().orElse(null);
                        if(ip!=null){
                            ip=mapForPassage.get(ip).getPassageName();
                        }
                        DeviceBO deviceBO = centDeviceConverter.toDeviceBOByKeTop(id, parkingLotDeviceInfoKeTopDTO, projectId, productId, productCategoryId,ip);
                        update.add(deviceBO);
                    },
                    () -> {
                        String ip = ipFromPassage.stream().filter(s -> {
                            if (StringUtils.equals(s, parkingLotDeviceInfoKeTopDTO.getDeviceIp())) {
                                return true;
                            } else {
                                return false;
                            }
                        }).findAny().orElse(null);
                        if(ip!=null){
                            ip=mapForPassage.get(ip).getPassageName();
                        }
                        DeviceBO deviceBO = centDeviceConverter.toDeviceBOByKeTop(parkingLotDeviceInfoKeTopDTO, projectId, productId, productCategoryId,ip);
                        add.add(deviceBO);
                    });
        });

        if (!add.isEmpty()) {
            queryDeviceSecondService.addDevices(add);
        }

        if (!update.isEmpty()) {
            queryDeviceSecondService.updateDevices(update);
            queryDeviceSecondService.updateDeviceStatus(update);
        }

        log.info("KeTop sync device end!");
    }

    @Override
    public VehicleCarportBO queryCarport(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, QueryVehicleCarportBO queryVehicleCarportBO) {

        FreeSpaceNumberKeTopDTO freeSpaceNumberKeTopDTO = keTopHttpService.queryFreeSpaceNumber(vehicleIntegratedConfigBO, new QueryFreeSpaceNumberKeTopDTO());
        return vehicleCarportConverter.freeSpaceNumberKeTopDTO2VehicleCarportBO(freeSpaceNumberKeTopDTO);
    }

//    @Override
//    public VehicleCarRecordInParkingLotBO queryCarRecordInParkingLot(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, QueryVehicleCarRecordInParkingLotBO queryVehicleCarRecordInParkingLotBO) {
//        Integer projectId = queryVehicleCarRecordInParkingLotBO.getProjectId();
//        String parkingLotName = queryVehicleCarRecordInParkingLotBO.getParkingLotName();
//
//        VehicleParkingLotSecondBO vehicleParkingLotSecondBO = vehicleParkingLotService.queryParkingLot(projectId, parkingLotName);
//
//        final int queryDays = 7;
//        Date date = new Date();
//        String endTime = AboutDateUtil.dateToString(date);
//        String startTime = AboutDateUtil.dateToString(AboutDateUtil.dateMinusDay(date, queryDays));
//        QueryCarInParkingLotKeTopDTO queryCarInParkingLotKeTopDTO = new QueryCarInParkingLotKeTopDTO(startTime, endTime);
//
//        CarInParkingLotKeTopDTO carInParkingLotKeTopDTO = keTopHttpService.queryCarInfo(vehicleIntegratedConfigBO, queryCarInParkingLotKeTopDTO);
//
//        Integer pageSize = queryCarInParkingLotKeTopDTO.getPageSize();
//        Integer totalCount = carInParkingLotKeTopDTO.getTotalCount();
//        int count = totalCount / pageSize;
//
//        List<CarInParkingLotKeTopDTO> carInParkingLotKeTopDTOList = new ArrayList<>();
//        carInParkingLotKeTopDTOList.add(carInParkingLotKeTopDTO);
//        for (int i = 0; i < count; i++) {
//            int initPageIndex = 2;
//
//            QueryCarInParkingLotKeTopDTO pageQuery = new QueryCarInParkingLotKeTopDTO(startTime, endTime);
//            pageQuery.setPageIndex(initPageIndex + i);
//
//            CarInParkingLotKeTopDTO pageResult = keTopHttpService.queryCarInfo(vehicleIntegratedConfigBO, pageQuery);
//            carInParkingLotKeTopDTOList.add(pageResult);
//        }
//
//        List<CarInfoKeTopDTO> carInfoKeTopDTOList = carInParkingLotKeTopDTOList.stream()
//                .flatMap(carInParkingLot -> carInParkingLot.getPlateList().stream())
//                .collect(Collectors.toList());
//
//        return vehicleCarConverter.toVehicleCarRecordInParkingLotBOByKeTop(carInfoKeTopDTOList, vehicleParkingLotSecondBO.getParkingLotName());
//    }
    @Override
    public VehicleCarRecordInParkingLotBO queryCarRecordInParkingLot(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, QueryVehicleCarRecordInParkingLotBO queryVehicleCarRecordInParkingLotBO) {
        Integer projectId = queryVehicleCarRecordInParkingLotBO.getProjectId();
        String parkingLotName = queryVehicleCarRecordInParkingLotBO.getParkingLotName();

        VehicleParkingLotSecondBO vehicleParkingLotSecondBO = vehicleParkingLotService.queryParkingLot(projectId, parkingLotName);

        FreeSpaceNumberExtKeTopDTO freeSpaceNumberExtKeTopDTO = keTopHttpService.queryFreeSpaceNumberExt(vehicleIntegratedConfigBO, new QueryFreeSpaceNumberKeTopDTO());
        List<CarOccupiedInfoKeTopDTO> carOccupiedInfoKeTopDTOList = Optional.ofNullable(freeSpaceNumberExtKeTopDTO.getCarOccupiedInfo()).orElse(new ArrayList<>());

        return vehicleCarConverter.toVehicleCarRecordInParkingLotBOByKeTop(carOccupiedInfoKeTopDTOList, vehicleParkingLotSecondBO.getParkingLotName());
    }

    @Async("threadPoolTaskExecutorForSchedule")
    @Override
    public void statisticsToBi(VehicleIntegratedConfigBO vehicleIntegratedConfigBO) {
        log.info("KeTop statistics to bi start!");

        try {
            Integer projectId = vehicleIntegratedConfigBO.getVehicleSystemInfoBO().getProjectId();

            VehicleParkingLot vehicleParkingLot = vehicleParkingLotMapper.selectOne(new LambdaQueryWrapper<VehicleParkingLot>().eq(VehicleParkingLot::getProjectId, projectId));
            String parkingLotName = vehicleParkingLot.getParkingLotName();
            String parkId = vehicleParkingLot.getParkId();

            Date processDate = new Date();
            Date ingestionDate = AboutDateUtil.dateMinusHour(AboutDateUtil.theSameDayHourOfDate(processDate, VehicleConstant.vehicleTimeZone), 1);
            Integer enterForCount = vehicleAccessRecordService.accessRecordForCount(vehicleAccessRecordConverter.toQueryVehicleAccessRecordForCountBOForEnter(projectId, parkId, ingestionDate));
            Integer goOutForCount = vehicleAccessRecordService.accessRecordForCount(vehicleAccessRecordConverter.toQueryVehicleAccessRecordForCountBOForGoOut(projectId, parkId, ingestionDate));
            Integer totalForCount = enterForCount + goOutForCount;
            biVehicleService.addDwsAccessRecordForHour(centBiConverter.toCreateBiVehicleAccessRecordHourFeignDTO(projectId, parkingLotName, totalForCount, enterForCount, goOutForCount, processDate, ingestionDate));

            Integer payTotalForSum = vehiclePayRecordService.payRecordForCount(vehiclePayRecordConverter.toQueryVehiclePayRecordForCountBO(projectId, parkId, ingestionDate));
            biVehicleService.addDwsPayRecordForHour(centBiConverter.toCreateBiVehiclePayRecordHourFeignDTO(projectId, parkingLotName, payTotalForSum, 0, 0, 0, 0, processDate, ingestionDate));
        } catch (Exception e) {
            log.error("科拓车场定时汇总数据到BI发生异常！异常原因：" + e.getMessage() + "所发生的的汇总错误的项目id：" + vehicleIntegratedConfigBO.getVehicleSystemInfoBO().getProjectId());
        }

        log.info("KeTop statistics to bi end!");
    }

    /**
     * 科拓的预约车位
     * @param vehicleIntegratedConfigBO
     * @param carVO
     * @return
     */
    @Override
    public String reserverSpace(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, CarVO carVO) {
        QueryReservableInfoKeTopDTO queryReservableInfoKeTopDTO = carVOtoQueryReservableInfoKeTopDTO(carVO);
        ReservableKeTopDTO reservableInfo = null;
        //调用科拓的接口，查询有空余车位的区域信息
        try{
            reservableInfo = keTopHttpService.getReservableInfo(vehicleIntegratedConfigBO, queryReservableInfoKeTopDTO);
        } catch (Exception e) {
            /**
             * 方便其他服务分清预约失败，由于接口报错和未配集成配置等异常的code一样
             */
            throw new DataException("预约失败");
        }
        //找到拥有剩余车位的区域信息
        List<ReservableInfoKeTopDTO> reservableInfoKeTopDTOS = reservableInfo.getReserveList();
        ReservableInfoKeTopDTO reservableInfoKeTopDTO = reservableInfoKeTopDTOS.stream().filter(s -> {
            if (s.getFree() > 0) {
                return true;
            } else {
                return false;
            }
        }).findAny().orElse(null);
        if(reservableInfoKeTopDTO==null){
            throw new DataException("暂无剩余车位");
        }else{
            QueryReserveSpaceKeTopDTO queryReserveSpaceKeTopDTO = carVO2toQueryReserveSpaceKeTopDTO(carVO, reservableInfoKeTopDTO);
            ReserveSpaceKeTopDTO reserveSpaceKeTopDTO = null;
            try {
                reserveSpaceKeTopDTO = keTopHttpService.reserveSpace(vehicleIntegratedConfigBO, queryReserveSpaceKeTopDTO);
            } catch (Exception e) {
                /**
                 * 方便其他服务分清预约失败，由于接口报错和未配集成配置等异常的code一样
                 */
                throw new DataException("预约失败");
            }
            return reserveSpaceKeTopDTO.getOrderNo();
        }
    }

    /**
     * 将CarVO对象转换为QueryReservableInfoKeTopDTO对象
     */
    private QueryReservableInfoKeTopDTO carVOtoQueryReservableInfoKeTopDTO(CarVO carVO){
        QueryReservableInfoKeTopDTO queryReservableInfoKeTopDTO = new QueryReservableInfoKeTopDTO();
        queryReservableInfoKeTopDTO.setEnterTime(AboutDateUtil.dateToString(carVO.getValidBeginTime()));
        queryReservableInfoKeTopDTO.setLeaveTime(AboutDateUtil.dateToString(carVO.getValidEndTime()));
        return queryReservableInfoKeTopDTO;
    }

    /**
     * 将CarVO对象和其他东西组装成QueryReserveSpaceKeTopDTO对象
     */
    private QueryReserveSpaceKeTopDTO carVO2toQueryReserveSpaceKeTopDTO(CarVO carVO,ReservableInfoKeTopDTO reservableInfoKeTopDTO){
        QueryReserveSpaceKeTopDTO queryReserveSpaceKeTopDTO = new QueryReserveSpaceKeTopDTO();
        queryReserveSpaceKeTopDTO.setPlateNo(carVO.getPlateNumber());
        queryReserveSpaceKeTopDTO.setType(KeyTopConstant.type);
        queryReserveSpaceKeTopDTO.setAddrId(reservableInfoKeTopDTO.getAddrId());
        queryReserveSpaceKeTopDTO.setEnterTime(AboutDateUtil.dateToString(carVO.getValidBeginTime()));
        queryReserveSpaceKeTopDTO.setLeaveTime(AboutDateUtil.dateToString(carVO.getValidEndTime()));
        queryReserveSpaceKeTopDTO.setUserName(carVO.getOwnerName());
        queryReserveSpaceKeTopDTO.setTel(carVO.getOwnerPhone());
        queryReserveSpaceKeTopDTO.setFailureTime(AboutDateUtil.dateToString(carVO.getValidEndTime()));
        queryReserveSpaceKeTopDTO.setFlag(KeyTopConstant.flag);
        return queryReserveSpaceKeTopDTO;
    }

    /**
     * 科拓的取消车位预约
     */
    @Override
    public void cancelReserve(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, String orderNo) {
        QueryCancelReserveKeTopDTO queryCancelReserveKeTopDTO = new QueryCancelReserveKeTopDTO();
        queryCancelReserveKeTopDTO.setOrderNo(orderNo);
        try {
            keTopHttpService.cancelReserveSpace(vehicleIntegratedConfigBO,queryCancelReserveKeTopDTO);
        } catch (Exception e) {
            /**
             * 方便其他服务分清预约失败，由于接口报错和未配集成配置等异常的code一样
             */
            throw new DataException("取消预约失败");
        }
    }

    /**
     * 开启道闸
     * @param vehicleIntegratedConfigBO
     * @param deviceControlDTO
     */
    @Override
    public void openDevice(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, DeviceControlDTO deviceControlDTO) {
        if(deviceControlDTO.getId()==null){
            throw new BusinessException("缺少通道id参数");
        }
        //根据通道id查询通道信息
        VehiclePassage vehiclePassage = vehiclePassageService.getById(deviceControlDTO.getId());
        //获取第三方的通道id
        String thirdIdForPassage = StringUtils.substringAfterLast(vehiclePassage.getThirdPassageId(), "-");
        QueryGateControlKeTopDTO queryGateControlKeTopDTO = toQueryGateControlKeTopDTO(thirdIdForPassage, openDevice);
        GateControlKeTopDTO gateControlKeTopDTO = keTopHttpService.gateControl(vehicleIntegratedConfigBO, queryGateControlKeTopDTO);
        if(gateControlKeTopDTO.getStatus()==-1){
            throw new BusinessException("道闸处于未知状态，无法开闸");
        }else if(gateControlKeTopDTO.getStatus()==1){
            throw new BusinessException("道闸正处于常开状态");
        }else{
            //表示成功
        }
    }

    /**
     * 关闭道闸
     * @param vehicleIntegratedConfigBO
     * @param deviceControlDTO
     */
    @Override
    public void closeDevice(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, DeviceControlDTO deviceControlDTO) {
        if(deviceControlDTO.getId()==null){
            throw new BusinessException("缺少通道id参数");
        }
        //根据通道id查询通道信息
        VehiclePassage vehiclePassage = vehiclePassageService.getById(deviceControlDTO.getId());
        //获取第三方的通道id
        String thridIdForPassage = StringUtils.substringAfterLast(vehiclePassage.getThirdPassageId(), "-");
        QueryGateControlKeTopDTO queryGateControlKeTopDTO = toQueryGateControlKeTopDTO(thridIdForPassage, closeDevice);
        GateControlKeTopDTO gateControlKeTopDTO = keTopHttpService.gateControl(vehicleIntegratedConfigBO, queryGateControlKeTopDTO);
        if(gateControlKeTopDTO.getStatus()==-1){
            throw new BusinessException("道闸处于未知状态，无法关闸");
        }else if (gateControlKeTopDTO.getStatus()==1){
            //如果为常开状态，就取消常开状态
            QueryGateControlKeTopDTO queryGateControlKeTopDTO1 = toQueryGateControlKeTopDTO(thridIdForPassage, cancelAllOpenDevice);
            GateControlKeTopDTO gateControlKeTopDTO1 = keTopHttpService.gateControl(vehicleIntegratedConfigBO, queryGateControlKeTopDTO1);
            if(gateControlKeTopDTO1.getStatus()==1){
                throw new BusinessException("道闸处于常开状态，取消常开状态失败");
            }
        }else{
            //表示成功
        }
    }

    /**
     * 常开道闸
     * @param vehicleIntegratedConfigBO
     * @param deviceControlDTO
     */
    @Override
    public void allOpenDevice(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, DeviceControlDTO deviceControlDTO) {
        if(deviceControlDTO.getId()==null){
            throw new BusinessException("缺少通道id参数");
        }
        //根据通道id查询通道信息
        VehiclePassage vehiclePassage = vehiclePassageService.getById(deviceControlDTO.getId());
        //获取第三方的通道id
        String thridIdForPassage = StringUtils.substringAfterLast(vehiclePassage.getThirdPassageId(), "-");
        QueryGateControlKeTopDTO queryGateControlKeTopDTO = toQueryGateControlKeTopDTO(thridIdForPassage, allOpenDevice);
        GateControlKeTopDTO gateControlKeTopDTO = keTopHttpService.gateControl(vehicleIntegratedConfigBO, queryGateControlKeTopDTO);
        if(gateControlKeTopDTO.getStatus()==-1){
            throw new BusinessException("道闸处于未知状态，无法常开道闸");
        }else if(gateControlKeTopDTO.getStatus()==0){
            throw new BusinessException("常开道闸失败");
        }else{
            //表示成功
        }
    }

    /**
     * 根据通道id生成QueryGateControlKeTopDTO
     */
    private QueryGateControlKeTopDTO toQueryGateControlKeTopDTO(String nodeId,String type){
        QueryGateControlKeTopDTO queryGateControlKeTopDTO = new QueryGateControlKeTopDTO();
        queryGateControlKeTopDTO.setNodeId(nodeId);
        queryGateControlKeTopDTO.setType(type);
        return queryGateControlKeTopDTO;
    }

    /**
     * 通过车牌号查询是否存在订单
     */
    @Override
    public String getCarBillInfo(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, String plateNumber) {
        CarBillInfoVO carBillInfoVO = new CarBillInfoVO();
        CarBillKeTopDTO carBillKeTopDTO = new CarBillKeTopDTO();
        carBillKeTopDTO.setPlateNo(plateNumber);
        String parkingPaymentInfo = keTopHttpService.getParkingPaymentInfo(vehicleIntegratedConfigBO, carBillKeTopDTO);
        if(!"0".equals(parkingPaymentInfo)){
            carBillInfoVO.setStatus(1);
        }
        return JSONObject.toJSONString(carBillInfoVO);
    }
}
